package it.unisannio.lesim.parser;

import it.unisannio.lesim.fsm.Condition;
import it.unisannio.lesim.fsm.FSM;
import it.unisannio.lesim.fsm.State;
import it.unisannio.lesim.fsm.Transition;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 *
 * @author Marco Calisti
 * @author Antonio Cuomo
 * @author Rocco Mazzeo
 * @author Francesco Pacilio
 * 
 */

public class ParserXML implements ParserXMLInterface{

    private Document doc;
    private String fileName;

    public ParserXML(String fileName) {
        this.fileName = fileName;
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setIgnoringElementContentWhitespace(true);
        DocumentBuilder builder;
        
        try {
            builder = factory.newDocumentBuilder();
            File xmlFile = new File(fileName);
            doc = builder.parse(xmlFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ArrayList readFSMS() {        
        NodeList fsms = doc.getElementsByTagName("FSM");
        ArrayList FSMS = new ArrayList();
        NodeList elem;
        if (fsms != null && fsms.getLength() > 0) {
            //Il ciclo itera per ogni macchina a stati finiti che viene letta dal file xml
            for (int i = 0; i < fsms.getLength(); i++) {
                Hashtable statesTemp = new Hashtable();

                //prelevo la prima macchina a stati finiti
                Element fsm = (Element) fsms.item(i);
                FSM f = new FSM(fsm.getElementsByTagName("Name").item(0).getTextContent(), fsm.getElementsByTagName("Description").item(0).getTextContent());
                //prelevo la lista di tutti gli stati
                NodeList states = ((Element) fsm.getElementsByTagName("States").item(0)).getElementsByTagName("State");

                if (states != null && states.getLength() > 0) {
                    //il ciclo itera per ogni stato trovato
                    for (int j = 0; j < states.getLength(); j++) {
                        Element state = (Element) states.item(j);
                        State s = new State(state.getElementsByTagName("Name").item(0).getTextContent(), state.getElementsByTagName("Description").item(0).getTextContent());
                        s.setIsFinal(Boolean.parseBoolean(state.getElementsByTagName("IsFinal").item(0).getTextContent().toLowerCase()));
                        s.setIsError(Boolean.parseBoolean(state.getElementsByTagName("IsError").item(0).getTextContent().toLowerCase()));
                        statesTemp.put(s.getName(), s);
                    }
                }

                //prelevo la lista di tutte le transizioni
                NodeList transitions = ((Element) fsm.getElementsByTagName("Transitions").item(0)).getElementsByTagName("Transition");

                if (transitions != null && transitions.getLength() > 0) {
                    //il ciclo itera per ogni stato trovato
                    for (int k = 0; k < transitions.getLength(); k++) {
                        Element transition = (Element) transitions.item(k);
                        
                        //aggiungo la transizione allo stato definito come StartState
                        String condition = null;
                        String action = null;
                        elem=transition.getElementsByTagName("Condition");
                        if(elem.getLength()>0){
                            condition = elem.item(0).getTextContent();
                        }
                        elem=transition.getElementsByTagName("Action");
                        if(elem.getLength()>0){
                            action = elem.item(0).getTextContent();
                        }
                        ((State) statesTemp.get(transition.getElementsByTagName("StartState").item(0).getTextContent())).addTransition(f, transition.getElementsByTagName("EndState").item(0).getTextContent(), condition, action);
                      //  ((State) statesTemp.get(transition.getElementsByTagName("StartState").item(0).getTextContent())).addTransition(transition.getElementsByTagName("EndState").item(0).getTextContent(), Integer.parseInt(transition.getElementsByTagName("ConditionTimeLess").item(0).getTextContent()), Integer.parseInt(transition.getElementsByTagName("ConditionTimeGreat").item(0).getTextContent()), transition.getElementsByTagName("ConditionType").item(0).getTextContent(), transition.getElementsByTagName("ConditionFieldExp").item(0).getTextContent());
                    }
                }
           
                Enumeration statesComplete = statesTemp.elements();
                while (statesComplete.hasMoreElements()) {
                    f.addState((State) statesComplete.nextElement());
                }
                f.setFirstState(fsm.getElementsByTagName("FirstState").item(0).getTextContent());

                FSMS.add(f);
            }
        }
        
        return FSMS;
    }
    
    public boolean writeFSMS(ArrayList fsms, String temp, String filename) {
        PrintStream ps = null;
        try {
            File file = new File(temp);
            ps = new PrintStream(new FileOutputStream(file));
            ps.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            ps.println("<!DOCTYPE SIP SYSTEM \"http://localhost:8080/AnalizzatoreSip/FSM.dtd\">");
            ps.println("<SIP>");
            if(fsms!=null){
                for(int i = 0; i<fsms.size(); i++)
                {
                    this.writeFSM(ps, (FSM)fsms.get(i));
                }
            }
            ps.println("</SIP>");
            ps.close();
             
            new File(filename).delete();
            file.renameTo(new File(filename));
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
            return false;
        }
        
        return true;
    }
    
    public void writeFSM(PrintStream ps, FSM fsm) {
        ps.println("\t<FSM>");
        ps.println("\t\t<Name>"+fsm.getName()+"</Name>");
        ps.println("\t\t<Description>"+fsm.getDescription()+"</Description>");
        ps.println("\t\t<States>");
        
        Enumeration enumeration = fsm.getStates();
        State state = null;
        while(enumeration.hasMoreElements()) {
            state = (State) enumeration.nextElement();

            ps.println("\t\t\t<State>");
            ps.println("\t\t\t\t<Name>" + state.getName() + "</Name>");
            ps.println("\t\t\t\t<Description>" + state.getDescription() + "</Description>");
            ps.println("\t\t\t\t<IsFinal>" + (state.isIsFinal() ? "True" : "False" ) + "</IsFinal>");
            ps.println("\t\t\t\t<IsError>" + (state.isIsError() ? "True" : "False" ) + "</IsError>");
            ps.println("\t\t\t</State>");
        }
        ps.println("\t\t</States>");
        
        ps.println("\t\t<FirstState>" + fsm.getCurrentState().getName() + "</FirstState>");
        ps.println("\t\t<Transitions>");
        
        enumeration = fsm.getStates();
        while(enumeration.hasMoreElements()) {
            state = (State) enumeration.nextElement();
            Transition trans = null;
            ArrayList transitions = state.getTransitions();
            for(int j=0; j<transitions.size(); j++){
                trans = (Transition) transitions.get(j);
                ps.println("\t\t\t<Transition>");     
                ps.println("\t\t\t\t<StartState>"+state.getName()+"</StartState>");
                ps.println("\t\t\t\t<EndState>"+trans.getNextState()+"</EndState>");

                if(trans.getCondition()!=null && !trans.getCondition().equals("")){
                    ps.println("\t\t\t\t<Condition>"+trans.getCondition().replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")+"</Condition>");
                }
      
                if(trans.getAction()!=null && !trans.getAction().equals("")){
                    ps.println("\t\t\t\t<Action>"+trans.getAction()+"</Action>");
                }
                ps.println("\t\t\t</Transition>");
            }  
        }
        
        ps.println("\t\t</Transitions>");
        ps.println("\t</FSM>");
    }
    
    public boolean addFSM(FSM fsm, String temp, String filename) {
        ArrayList fsms = this.readFSMS();
        fsms.add(fsm);
        return writeFSMS(fsms, temp, filename);
    }
    
}